package org.dromara.article.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import cn.hutool.system.SystemUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.dromara.article.domain.Article;
import org.dromara.article.domain.ArticleUploadLog;
import org.dromara.article.domain.vo.ArticleQueryVo;
import org.dromara.article.domain.vo.ArticleVo;
import org.dromara.article.emnu.ArticleTypeConstants;
import org.dromara.article.mapper.ArticleDataMapper;
import org.dromara.article.mapper.ArticleMapper;
import org.dromara.article.mapper.ArticleUploadLogMapper;
import org.dromara.article.mapper.KeywordsDictMapper;
import org.dromara.common.core.domain.R;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 文章接口逻辑
 */
@Slf4j
@Service
public class ArticleService {

    // 下载线程池
    private final ExecutorService downloadExecutor = Executors.newSingleThreadExecutor();


    @Resource
    private ArticleMapper articleMapper;
    @Resource
    private ArticleUploadLogMapper logMapper;
    @Resource
    private ArticleDataMapper articleDataMapper;
    @Resource
    private KeywordsDictMapper keywordsDictMapper;

    /**
     * 文章上传
     *
     * @param file
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public R<Void> uploadArticle(MultipartFile file, String category) {
        if (file.getSize() == 0) {
            return R.fail("上传文件不能为空");
        }

        // 获得文件类型
        String fileType = StrUtil.split(file.getOriginalFilename(), ".").get(1).toLowerCase();

        boolean verifyType = verifyFileType(fileType);
        if (!verifyType) {
            return R.fail("上传文件格式错误!");
        }

        // 开始保存文件
        String savePath = getSavePath(category);
        if (savePath.equals("error")) {
            return R.fail("系统不支持");
        }

        // 保存压缩包到上传文件,创建临时目录
        long timestamp = System.currentTimeMillis();
        String saveFolder = savePath + "\\" + timestamp;
        FileUtil.mkdir(saveFolder); // 创建目录
        String filePath = saveFolder + "\\" + file.getOriginalFilename();
        try {
            file.transferTo(FileUtil.file(filePath));
        } catch (Exception e) {
            log.error("文件转存错误");
            return R.fail("文件转存错误");
        }

        // 解压文件
        boolean unZipStatus = unZipFile(saveFolder, filePath, fileType);
        if (!unZipStatus) {
            return R.fail("文件解压失败,请重新上传");
        }

        // 文件解压成功,开始清理文件,并将文章进行编码,然后移动到新位置,并添加上传记录
        List<String> list = FileUtil.listFileNames(saveFolder);
        int uploadSize = list.size(); // 上传文件数量
        List<String> keywordsList = keywordsDictMapper.selectKeywords(2, 0);
        if (CollUtil.isNotEmpty(keywordsList)) {
            Iterator<String> iterator = list.iterator();
            while (iterator.hasNext()) {
                keywordsList.forEach(keywords -> {
                    if (iterator.next().contains(keywords)) {
                        log.info("文件名包含关键词,删除文件,关键词:{},文件名:{}", keywords, iterator.next());
                        FileUtil.del(saveFolder + "\\" + iterator.next());
                    }
                });
            }

            log.info("文件删除完毕,根据文件大小删除文章");
            List<File> files = FileUtil.loopFiles(saveFolder);
            files.forEach(tmpFile -> {
                long size = FileUtil.size(tmpFile);
                if (size < 4 * 1024 || size > 6 * 1024) {
                    log.info("文件大小错误，删除文件：{}", tmpFile.getName());
                    FileUtil.del(tmpFile);
                }

                List<String> contents = FileUtil.readLines(tmpFile, "UTF-8");
                // 处理文章中的内容
                Iterator<String> contentIterator = contents.iterator();
                boolean contains = false;
                while (contentIterator.hasNext()) {
                    String text = contentIterator.next();
                    // 删除app-creator的英文思考内容
                    if (text.startsWith("I")) {
                        contentIterator.remove();
                    }

                    // 删除AI默认写作的一级标题,加快排版速度
                    if (text.startsWith("#")) {
                        contentIterator.remove();
                    }

                    // 判断文章是否包含图片
                    if (text.contains("http://img2.idouq.com")) {
                        contains = true;
                        break;
                    }
                }

                log.info("检查文件是否包含图片");
                if (!Objects.equals(category, "职场") && !contains) {
                    log.info("非职场赛道文章没有图片,删除文章:{}", tmpFile.getName());
                    FileUtil.del(tmpFile);
                }

            });

            log.info("文章筛除完成,开始对文章编码,并移动到下载目录下");
        }

        int afterCleanNum = FileUtil.listFileNames(saveFolder).size();

        ArticleUploadLog uploadLog = new ArticleUploadLog();
        uploadLog.setCreatedBy(1L);
        uploadLog.setUploadNum(uploadSize);
        uploadLog.setUseNum(afterCleanNum);
        uploadLog.setDisNum(uploadSize - afterCleanNum);
        uploadLog.setTypeId(ArticleTypeConstants.getIdByName(category));
        uploadLog.setCreateUser(1);
        uploadLog.setCreateTime(new Date());
        uploadLog.setCreateUser(1);
        logMapper.insert(uploadLog);

        // 拼接保存目录
        String downloadPath = getDownloadPath(category);
        if (downloadPath != "error") {
            FileUtil.mkdir(downloadPath);
            List<File> waitMoveFiles = FileUtil.loopFiles(saveFolder);

            for (File tmpFile : waitMoveFiles) {
                Article article = new Article();
                String title = tmpFile.getName().split(".md")[0];
                article.setTitle(title);
                article.setStatus(0); // 未领取
                article.setTypeId(ArticleTypeConstants.getIdByName(category));
                article.setCreateTime(new Date());
                article.setDiskPath(downloadPath + "\\" + tmpFile.getName());
                article.setIsPublished(0); // 未发布
                articleMapper.insert(article);

                FileUtil.move(tmpFile, new File(downloadPath + "\\" + tmpFile.getName()), true);
            }

            log.info("文件移动完成,删除临时目录");
            FileUtil.del(saveFolder);
        } else {
            return R.fail("创建下载目录失败,请检查服务器状态");
        }


        return R.ok("上传成功");
    }

    /**
     * 获得下载目录位置
     *
     * @param type
     * @return
     */
    private String getDownloadPath(String type) {
        String osName = SystemUtil.getOsInfo().getName().toLowerCase();
        String downloadPath = "error";
        if (osName.contains("windows")) {
            downloadPath = "D:\\work\\download\\" + type + "\\" + DateUtil.format(new Date(), "yyyyMMdd");
        } else if (osName.contains("linux")) {
            downloadPath = "/usr/local/work/download/" + type + "/" + DateUtil.format(new Date(), "yyyyMMdd");
        }
        return downloadPath;
    }

    /**
     * 解压文件
     *
     * @param filePath 文件路径
     * @return
     */
    private boolean unZipFile(String saveFolder, String filePath, String fileType) {
        // File file = new File(filePath);
        switch (fileType) {
            case "7z" -> {
                // 解压7z格式压缩包
                // RandomAccessFile randomAccessFile = new RandomAccessFile(filePath, "r");

            }
            case "zip" -> {
                // 解压zip格式压缩包
                File unzip = ZipUtil.unzip(filePath, saveFolder);
                FileUtil.del(filePath); // 删除原文件
                if (FileUtil.listFileNames(unzip.getPath()).isEmpty()) {
                    return false;
                }
                return true;
            }
        }
        return false;
    }

    /**
     * 获取保存路径
     *
     * @param type 文章类型
     * @return
     */
    public String getSavePath(String type) {
        String osName = SystemUtil.getOsInfo().getName();
        if (osName.toLowerCase().contains("windows")) {
            return "D:\\文章上传临时目录" + "\\" + type;
        } else if (osName.toLowerCase().contains("linux")) {
            return "/usr/local/baowen/文章临时上传目录/" + type;
        }
        return "error";
    }

    /**
     * 校验上传文件的格式
     *
     * @param fileType
     * @return
     */
    private boolean verifyFileType(String fileType) {
        return switch (fileType) {
            case "7z", "zip", "rar" -> true;
            default -> false;
        };
    }

    /**
     * 查询文章列表
     *
     * @param queryVo
     * @param pageQuery
     */
    public TableDataInfo<ArticleVo> getArticleList(ArticleQueryVo queryVo, PageQuery pageQuery) {
        if (null == queryVo) queryVo = new ArticleQueryVo();
        if (null != queryVo.getStatus() && queryVo.getStatus() == 1) {
            queryVo.setUserId(LoginHelper.getUserId());
        } else if (null == queryVo.getStatus()) {
            queryVo.setStatus(0); // 默认查询未领取的文章
        }

        IPage<ArticleVo> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
        IPage<ArticleVo> pageResult = articleMapper.selectArticleList(queryVo, page);
        return TableDataInfo.build(pageResult);
    }

    /**
     * 下载文件
     *
     * @param ids
     * @param response
     */
    @Transactional(rollbackFor = Exception.class)
    public void downloadArticle(List<String> ids, HttpServletResponse response) {

        downloadExecutor.submit(() -> {
            // 查询文章ID中是否有已被领取过的,如果有,则自动更换为其他ID的数据
            List<Article> articles = articleMapper.selectList(new LambdaQueryWrapper<Article>().select(Article::getId, Article::getStatus, Article::getId, Article::getTypeId).in(Article::getId, ids));

            AtomicInteger counter = new AtomicInteger();

            List<Long> idList = new ArrayList<>();
            articles.forEach(article -> {
                if (article.getStatus() == 1) {
                    // 文章已被领取
                    counter.addAndGet(1);
                } else {
                    idList.add(article.getId());
                }
            });

            if (counter.get() > 0) {
                // 更换成其他的数据
                List<Long> unUseIds = articleMapper.selectIds(articles.get(0).getTypeId(), counter.get());
                idList.addAll(unUseIds);
            }

            // 查询文章的信息,然后统一打包扔到前端,并对这些数据做状态更新
            List<Article> articleList = articleMapper.selectList(new QueryWrapper<Article>().select("disk_path").in("id", idList));
            Long userId = LoginHelper.getUserId(); // 领取人
            Date collTime = new Date(); // 领取时间

            // 压缩文件
        });
    }

    /**
     * 返回单篇文章内容
     *
     * @param articleId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public R<String> getContent(String articleId) {
        if (StrUtil.isBlank(articleId)) {
            return R.fail("文章ID不能为空");
        }

        Article article = articleMapper.selectOne(new QueryWrapper<Article>().select("id", "disk_path").eq("id", articleId));
        if (null == article) {
            return R.fail("参数错误,文章不存在");
        }

        List<String> list = FileUtil.readLines(new File(article.getDiskPath()), "UTF-8");
        StringBuilder sb = new StringBuilder();
        list.forEach(line -> sb.append(line).append("\n"));

        // 修改发布状态
        articleMapper.updatePublishedStatus(1, articleId, new Date());

        return R.ok("获取成功", sb.toString());
    }

    /**
     * 领取文章
     *
     * @param articleId
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public R<Void> claim(String articleId) {
        synchronized (articleId) {
            String[] articleIds = articleId.split(",");

            List<Article> articleList = new ArrayList<>(articleIds.length);
            if (ArrayUtil.isNotEmpty(articleIds)) {
                for (String id : articleIds) {
                    // 判断此文章是否已经被领取,如果被领取,则直接返回失败,前端刷新文章列表页面
                    Article article = articleMapper.selectById(id);
                    if (null == article) return R.fail("文章数据错误,请刷新重试");
                    if (article.getStatus() != 0) return R.fail("此文章已被领取,请刷新重试");
                    articleList.add(article);
                }
            }


            for (Article article : articleList) {
                // 判断此文章是否已经被领取,如果被领取,则直接返回失败,前端刷新文章列表页面
                article.setStatus(1); // 状态修改为已领取
                article.setCollectionTime(new Date());
                Long userId = LoginHelper.getUserId();
                article.setUserId(userId);

                article.setUpdateTime(new Date());
                article.setUpdatedBy(userId);
            }

            articleMapper.claimUpdateBatch(articleList);

        }
        return R.ok("领取成功");
    }

    /**
     * 废弃文章
     *
     * @param articleIds 文章编号
     * @param disReason  废弃原因
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    public R<Void> disuse(String disReason, String articleIds) {
        // 查询文章状态,如果状态不是已领取,则不能直接废弃
        if (StrUtil.isNotBlank(articleIds)) {
            String[] articleIdArr = articleIds.split(",");
            if (ArrayUtil.isNotEmpty(articleIdArr)) {
                for (String articleId : articleIdArr) {
                    Article article = articleMapper.selectById(articleId);
                    if (null != article) {
                        article.setStatus(2); // 状态: 废弃
                        article.setDisReason(disReason); // 废弃原因
                        article.setUpdateTime(new Date());
                        article.setUpdatedBy(LoginHelper.getUserId());
                        article.setDiscardedTime(new Date());
                        articleMapper.updateById(article);
                    }
                }

                return R.ok("文章已废弃，请重新领取");
            }
        }

        return R.fail("参数错误");

    }
}
